/* SPDX-License-Identifier: GPL-2.0 */
#ifndef SEMINIX_MM_TYPES_H
#define SEMINIX_MM_TYPES_H

#ifndef __ASSEMBLY__

#include <utils/log2.h>
#include <utils/atomic.h>
#include <utils/list.h>
#include <utils/rbtree.h>
#include <seminix/bug.h>
#include <seminix/rwsem.h>
#include <asm/pgtable-types.h>
#include <asm/mmu.h>

struct mm_struct;

/*
 * Each physical page in the system has a struct page associated with
 * it to keep track of whatever it is we are using the page for at the
 * moment. Note that we have no way to track which tasks are using
 * a page, though if it is a pagecache page, rmap structures can tell us
 * who is mapping it.
 *
 * If you allocate the page using alloc_pages(), you can use some of the
 * space in struct page for your own purposes.  The five words in the main
 * union are available, except for bit 0 of the first word which must be
 * kept clear.  Many users use this word to store a pointer to an object
 * which is guaranteed to be aligned.  If you use the same storage as
 * page->mapping, you must restore it to NULL before freeing the page.
 *
 * If your page will not be mapped to userspace, you can also use the four
 * bytes in the mapcount union, but you must call page_mapcount_reset()
 * before freeing it.
 *
 * If you want to use the refcount field, it must be used in such a way
 * that other CPUs temporarily incrementing and then decrementing the
 * refcount does not cause problems.  On receiving the page from
 * alloc_pages(), the refcount will be positive.
 *
 * If you allocate pages of order > 0, you can use some of the fields
 * in each subpage, but you may need to restore some of their values
 * afterwards.
 *
 * SLUB uses cmpxchg_double() to atomically update its freelist and counters.
 * That requires that freelist & counters in struct slab be adjacent and
 * double-word aligned. Because struct slab currently just reinterprets the
 * bits of struct page, we align all struct pages to double-word boundaries,
 * and ensure that 'freelist' is aligned within struct slab.
 */
#ifdef CONFIG_HAVE_ALIGNED_STRUCT_PAGE
#define _struct_page_alignment	__aligned(2 * sizeof(unsigned long))
#else
#define _struct_page_alignment
#endif

struct page {
    unsigned long flags;

    union {
        struct {    /* buddy 系统使用 */
            struct list_head lru;
            unsigned long private;  /* order */
        };
        struct {    /* slab 系统使用 */
            struct kmem_cache *slab;
            void *freelist;
            unsigned inuse;
        };
        struct {    /* buddy 系统复合页描述 */
            unsigned long compound_head;    /* Bit zero is set */
            unsigned char compound_order;
        };
    };

    union {
        atomic_t _mapcount;
        unsigned int page_type;
    };

    /* Usage count. *DO NOT USE DIRECTLY*. See page_ref.h */
    atomic_t _refcount;
} _struct_page_alignment;

#include <seminix/page-flags-layout.h>
#include <seminix/page-flags.h>
#include <seminix/page_ref.h>

/*
 * Used for sizing the vmemmap region on some architectures
 */
#define STRUCT_PAGE_MAX_SHIFT	(order_base_2(sizeof(struct page)))

struct vm_area_struct {
    unsigned long   vm_start;
    unsigned long   vm_end;

    struct vm_area_struct *vm_next, *vm_prev;

    struct rb_node vm_rb;
    unsigned long rb_subtree;
    struct mm_struct *vm_mm;
    pgprot_t vm_page_prot;
    unsigned long vm_flags;

    unsigned long page_size;
    struct page **pages;
    int nr_pages;
};

struct mm_struct {
    struct vm_area_struct *mmap;
    struct rb_root mm_rb;
    u64 vmacache_seqnum;
    unsigned long highest_vm_end;
    pgd_t *pgd;

    atomic_t mm_users;

    atomic_long_t pgtables_bytes;	/* PTE page table pages */
    int map_count;			/* number of VMAs */
    spinlock_t page_table_lock;
    struct rw_semaphore mmap_sem;

    spinlock_t  tl_lock;
    struct list_head thread_list;  /* share all thread list */

    /* Architecture-specific MM context */
    mm_context_t context;

    atomic_t tlb_flush_pending;
};

extern struct mm_struct init_mm;

static inline void init_tlb_flush_pending(struct mm_struct *mm)
{
    atomic_set(&mm->tlb_flush_pending, 0);
}

static inline void inc_tlb_flush_pending(struct mm_struct *mm)
{
    atomic_inc(&mm->tlb_flush_pending);
    /*
     * The only time this value is relevant is when there are indeed pages
     * to flush. And we'll only flush pages after changing them, which
     * requires the PTL.
     *
     * So the ordering here is:
     *
     *	atomic_inc(&mm->tlb_flush_pending);
     *	spin_lock(&ptl);
     *	...
     *	set_pte_at();
     *	spin_unlock(&ptl);
     *
     *				spin_lock(&ptl)
     *				mm_tlb_flush_pending();
     *				....
     *				spin_unlock(&ptl);
     *
     *	flush_tlb_range();
     *	atomic_dec(&mm->tlb_flush_pending);
     *
     * Where the increment if constrained by the PTL unlock, it thus
     * ensures that the increment is visible if the PTE modification is
     * visible. After all, if there is no PTE modification, nobody cares
     * about TLB flushes either.
     *
     * This very much relies on users (mm_tlb_flush_pending() and
     * mm_tlb_flush_nested()) only caring about _specific_ PTEs (and
     * therefore specific PTLs), because with SPLIT_PTE_PTLOCKS and RCpc
     * locks (PPC) the unlock of one doesn't order against the lock of
     * another PTL.
     *
     * The decrement is ordered by the flush_tlb_range(), such that
     * mm_tlb_flush_pending() will not return false unless all flushes have
     * completed.
     */
}

static inline void dec_tlb_flush_pending(struct mm_struct *mm)
{
    /*
     * See inc_tlb_flush_pending().
     *
     * This cannot be smp_mb__before_atomic() because smp_mb() simply does
     * not order against TLB invalidate completion, which is what we need.
     *
     * Therefore we must rely on tlb_flush_*() to guarantee order.
     */
    atomic_dec(&mm->tlb_flush_pending);
}

static inline bool mm_tlb_flush_pending(struct mm_struct *mm)
{
    /*
     * Must be called after having acquired the PTL; orders against that
     * PTLs release and therefore ensures that if we observe the modified
     * PTE we must also observe the increment from inc_tlb_flush_pending().
     *
     * That is, it only guarantees to return true if there is a flush
     * pending for _this_ PTL.
     */
    return atomic_read(&mm->tlb_flush_pending);
}

static inline bool mm_tlb_flush_nested(struct mm_struct *mm)
{
    /*
     * Similar to mm_tlb_flush_pending(), we must have acquired the PTL
     * for which there is a TLB flush pending in order to guarantee
     * we've seen both that PTE modification and the increment.
     *
     * (no requirement on actually still holding the PTL, that is irrelevant)
     */
    return atomic_read(&mm->tlb_flush_pending) > 1;
}

/*
 * The per task VMA cache array:
 */
#define VMACACHE_BITS 2
#define VMACACHE_SIZE (1U << VMACACHE_BITS)
#define VMACACHE_MASK (VMACACHE_SIZE - 1)

struct vmacache {
    u64 seqnum;
    struct vm_area_struct *vmas[VMACACHE_SIZE];
};

#endif /* !__ASSEMBLY__ */
#endif /* !SEMINIX_MM_TYPES_H */
